package imp

import (
	sqlc "bgs/db/sqlc"
	"bgs/db/sqlc/ctype"
	activityGrpc "bgs/grpc/gen/service/activity"
	// commonActivityGrpc "bgs/grpc/gen/shared/common_activity"
	"bgs/grpc/gen/shared/message"
	activityService "bgs/service/activity"
	"bgs/service/model"
	"bgs/util"
	"encoding/json"
	"fmt"

	"context"
	"database/sql"
)

/****************************************************************************************/
/*                               store activity entity of helper                        */
/****************************************************************************************/

func transformActivityOfStoreEntityToPb(
	activityProductWithProductInfos []sqlc.ListNormalActivityProductWithProductInfosByActivityIDRow,
	activitySkuWithProductOrProductSpecInfos []*model.ActivitySkuWithProductOrProductSpecInfo,
	activityConsignmentPtr *sqlc.ActivityConsignment,
	activityConsignmentBonusSettings []sqlc.ActivityConsignmentBonusSetting) func(sqlc.Activity) *activityGrpc.ActivityOfStoreEntity {
	return func(o sqlc.Activity) *activityGrpc.ActivityOfStoreEntity {
		v := &activityGrpc.ActivityOfStoreEntity{
			Id:                      o.ID,
			BrandId:                 o.BrandID,
			StoreId:                 o.StoreID.Int64,
			DistributorId:           o.DistributorID.Int64,
			Name:                    o.Name,
			Type:                    o.Type,
			Intro:                   string(o.Intro),
			ProcessStatus:           o.ProcessStatus,
			Published:               sqlc.GoNTimeToPBTimestamp(o.Published),
			Begined:                 sqlc.GoTimeToPBTimestamp(o.Begined),
			Ended:                   sqlc.GoTimeToPBTimestamp(o.Ended),
			TimeRanges:              transformTimeRangeEntityToPb(o.TimeRanges),
			Background:              o.Background.String,
			Poster:                  o.Poster.String,
			SaleMode:                o.SaleMode,
			VerifyQualification:     o.VerifyQualification,
			QualifyTypes:            o.QualifyTypes,
			MaxiumPurchaseFrequency: o.MaxiumPurchaseFrequency.Int32,
			MinimumPurchaseAmount:   o.MinimumPurchaseAmount,
			Tel:                     o.Tel,
			Owner:                   o.Owner,
			ActivityProductWithSkus: mapActivityProductWithProductInfoToPb(activityProductWithProductInfos, activitySkuWithProductOrProductSpecInfos, transActivityProductWithProductInfoToPb),
		}

		if activityService.CheckCanEndorse(o.Type) {
			v.Consignment = transActivityConfignmentToPb(*activityConsignmentPtr, activityConsignmentBonusSettings)
		}

		return v
	}
}

/****************************************************************************************/
/*                               store activity entity of cmd                           */
/****************************************************************************************/

// CreateActivityOfStore store新增活动
func (s *ActivityAPIService) CreateActivityOfStore(ctx context.Context, req *activityGrpc.CreateActivityOfStoreRequest) (res *activityGrpc.CreateActivityOfStoreResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("activityStore->CreateActivityOfStore:%v", req)
	res = &activityGrpc.CreateActivityOfStoreResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}
	var activity sqlc.Activity
	var activityWarehouses []sqlc.ActivityWarehouse
	// var activityProducts []sqlc.ActivityProduct
	var activityProductWithProductInfos []sqlc.ListNormalActivityProductWithProductInfosByActivityIDRow
	// var activitySkus []sqlc.ActivitySku
	var activitySkuWithProductOrProductSpecInfos []*model.ActivitySkuWithProductOrProductSpecInfo
	var activityConsignmentPtr *sqlc.ActivityConsignment
	var activityConsignmentBonusSettings []sqlc.ActivityConsignmentBonusSetting
	err = s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {

		storeID := req.StoreId
		storePtr, err := s.storeService.GetStorePtr(ctx, q, storeID)
		if err != nil {
			return
		}

		if !storePtr.DistributorID.Valid {
			err = fmt.Errorf("不是分销门店")
			return
		}

		distributorPtr, err := s.brandService.GetDistributorPtr(ctx, q, storePtr.DistributorID.Int64)
		if err != nil {
			return
		}
		if distributorPtr.Status != string(sqlc.EmRecordStatusNormal) {
			err = fmt.Errorf("分销门店所属的owner用户已不是品牌分销员")
			return
		}

		brandID := storePtr.BrandID
		brandDistributeSupplySettingPtr, sqlErr := s.brandService.GetBrandDistributeSupplySettingPtr(ctx, q, brandID)
		if sqlErr != nil {
			if sql.ErrNoRows == sqlErr {
				err = fmt.Errorf("无法找到品牌的供货设置")
			} else {
				err = sqlErr
			}
			return
		}

		p := sqlc.CreateActivityParams{
			BrandID:                       brandID,
			StoreID:                       sqlc.NInt64FromInt64(storeID),
			DistributorID:                 storePtr.DistributorID,
			PubUserID:                     req.PubUserId,
			Name:                          req.Name,
			Type:                          req.Type,
			Intro:                         json.RawMessage(req.Intro),
			Begined:                       sqlc.PBTimestampToGoTime(req.Begined),
			Ended:                         sqlc.PBTimestampToGoTime(req.Ended),
			Background:                    sqlc.NSFromString(req.Background),
			SaleMode:                      req.SaleMode,
			DeliveryModes:                 brandDistributeSupplySettingPtr.DeliveryModes,
			IsUseWarehouseShippingFeeRule: brandDistributeSupplySettingPtr.IsUseWarehouseShippingFeeRule,
			UnifiedShippingFee:            brandDistributeSupplySettingPtr.UnifiedShippingFee,
			MaxiumPurchaseFrequency:       sqlc.NInt32FromInt32(req.MaxiumPurchaseFrequency),
			MinimumPurchaseAmount:         req.MinimumPurchaseAmount,
			Tel:                           req.Tel,
			Owner:                         string(sqlc.EmActivityOwnerStore),
		}

		if len(req.TimeRanges) > 0 {
			var timeRanges []ctype.TimeRange
			for _, tr := range req.TimeRanges {
				timeRanges = append(timeRanges, ctype.TimeRange{
					BeginAt: tr.BeginAt,
					EndAt:   tr.EndAt,
				})
			}
			p.TimeRanges = timeRanges
		}

		activity, err = q.CreateActivity(ctx, p)
		if err != nil {
			return err
		}
		slog.Infof("1 创建门店活动 ok")

		// 活动仓库

		brandDistributeWarehousePtrs, err := s.brandService.ListBrandDistributeWarehousePtrsByBrandID(ctx, q, brandID)
		if err != nil {
			return
		}
		if len(brandDistributeWarehousePtrs) == 0 {
			err = fmt.Errorf("供货门店没有设置")
			return
		}

		var warehouse sqlc.ActivityWarehouse
		for _, ptr := range brandDistributeWarehousePtrs {
			warehouse, err = q.UpsertActivityWarehouse(ctx, sqlc.UpsertActivityWarehouseParams{
				BrandID:          activity.BrandID,
				ActivityID:       activity.ID,
				RootActivityID:   activity.RootID,
				ParentActivityID: activity.ParentID,
				WarehouseID:      ptr.WarehouseID,
			})
			if err != nil {
				return err
			}
			activityWarehouses = append(activityWarehouses, warehouse)
		}
		slog.Infof("2 设置门店活动仓库 ok")

		// 活动商品、商品规格
		var activityProduct sqlc.ActivityProduct
		// var activitySku sqlc.ActivitySku
		for idx1, item1 := range req.ActivityProductWithSkus {
			maxSku, minSku := util.MaxWithMin(util.ToGenericSlice(item1.Skus), func(i1, i2 interface{}) util.Ord {
				ps1, ps2 := i1.(*activityGrpc.ActivitySkuEntity), i2.(*activityGrpc.ActivitySkuEntity)
				return (util.IfThenElse(ps1.Price-ps2.Price < 0, util.Lt, util.Gt)).(util.Ord)
			})
			priceMax, priceMin := maxSku.(*activityGrpc.ActivitySkuEntity).Price, minSku.(*activityGrpc.ActivitySkuEntity).Price
			slog.Infof("max,min %v, %v", priceMax, priceMin)
			quantity := util.Int32IfThenElse(item1.Quantity == 0, 1, item1.Quantity)

			activityProduct, err = q.UpsertActivityProduct(ctx, sqlc.UpsertActivityProductParams{
				BrandID:          activity.BrandID,
				ActivityID:       activity.ID,
				RootActivityID:   activity.RootID,
				ParentActivityID: activity.ParentID,
				ProductID:        item1.ProductId,
				IsShow:           item1.IsShow,
				PriceMin:         priceMin,
				PriceMax:         priceMax,
				Quantity:         quantity,
				SortNo:           int32(idx1 + 1),
			})
			if err != nil {
				return err
			}
			// activityProducts = append(activityProducts, activityProduct)

			for idx2, item2 := range item1.Skus {
				_, err = q.UpsertActivitySku(ctx, sqlc.UpsertActivitySkuParams{
					BrandID:           activity.BrandID,
					ActivityID:        activity.ID,
					RootActivityID:    activity.RootID,
					ParentActivityID:  activity.ParentID,
					ActivityProductID: activityProduct.ID,
					ProductID:         item2.ProductId,
					ProductSpecID:     sqlc.NInt64FromInt64(item2.ProductSpecId),
					Price:             item2.Price,
					Maximum:           util.Int32IfThenElse(item2.Maximum == 0, -1, item2.Maximum),
					ProductSpecKey:    fmt.Sprintf("%d-%d", item2.ProductId, item2.ProductSpecId),
					SortNo:            int32(idx2 + 1),
				})
				if err != nil {
					return err
				}

				// activitySkus = append(activitySkus, activitySku)
			}

		}
		slog.Infof("3 设置活动B商品、商品规格 ok")

		_, err = q.UpdateActivitySkuStoreKeyByActivityID(ctx, activity.ID)
		if err != nil {
			return err
		}
		slog.Infof("3.5 更新storageKey ok")

		if activityService.CheckCanEndorse(activity.Type) {
			// 活动代卖设置
			activityConsignment, err2 := q.UpsertActivityConsignment(ctx, sqlc.UpsertActivityConsignmentParams{
				ActivityID:           activity.ID,
				AllowHiddenProduct:   req.Consignment.AllowHiddenProduct,
				AllowAllSpokesman:    req.Consignment.AllowAllSpokesman,
				SpecificSpokesmanIds: req.Consignment.SpecificSpokesmanIds,
				// SpecificSpokesmans: trans.MapMiniItemFromPb(req.Consignment.SpecificSpokesmans, trans.MiniItemFromPb),
			})
			if err2 != nil {
				return err2
			}
			activityConsignmentPtr = &activityConsignment
			slog.Info("4 设置活动B帮卖 ok")

			//3 活动代卖奖励设置
			var bonusSetting sqlc.ActivityConsignmentBonusSetting
			for _, item := range req.Consignment.ConsignmentBonusSettings {
				bonusSetting, err = q.UpsertActivityConsignmentBonusSetting(ctx, sqlc.UpsertActivityConsignmentBonusSettingParams{
					ActivityID: activity.ID,
					ProductID:  item.ProductId,
					BonusRate:  item.BonusRate,
				})
				if err != nil {
					return err
				}
				activityConsignmentBonusSettings = append(activityConsignmentBonusSettings, bonusSetting)
			}
			slog.Info("5 设置门店活动帮卖奖励 ok")
		}

		activityProductWithProductInfos, activitySkuWithProductOrProductSpecInfos, err = s.activityService.FetchNormalProductAndSkus(ctx, q, activity.ID)
		if err != nil {
			return err
		}
		slog.Info("6 读取门店活动产品及sku信息 ok ok")

		return
	})

	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	res.ActivityOfStore = transformActivityOfStoreEntityToPb(activityProductWithProductInfos, activitySkuWithProductOrProductSpecInfos, activityConsignmentPtr, activityConsignmentBonusSettings)(activity)
	return
}

// UpdateActivityOfB B更新活动
func (s *ActivityAPIService) UpdateActivityOfStore(ctx context.Context, req *activityGrpc.UpdateActivityOfStoreRequest) (res *activityGrpc.UpdateActivityOfStoreResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("activityStore->UpdateActivityOfStore:%v", req)
	res = &activityGrpc.UpdateActivityOfStoreResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}
	q := s.dao.Q
	var activity sqlc.Activity
	var activityWarehouses []sqlc.ActivityWarehouse
	// var activityProducts []sqlc.ActivityProduct
	var activityProductWithProductInfos []sqlc.ListNormalActivityProductWithProductInfosByActivityIDRow
	// var activitySkus []sqlc.ActivitySku
	var activitySkuWithProductOrProductSpecInfos []*model.ActivitySkuWithProductOrProductSpecInfo
	var activityConsignmentPtr *sqlc.ActivityConsignment
	var activityConsignmentBonusSettings []sqlc.ActivityConsignmentBonusSetting
	var toDisableActivityConsignmentBonusSettingIDs []int64
	activityID := req.Id
	storeID := req.StoreId

	activity, err = q.GetActivity(ctx, activityID)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	if !activity.StoreID.Valid || activity.StoreID.Int64 != storeID {
		err = fmt.Errorf("活动不属于当前门店")
		return
	}

	brandID := activity.BrandID
	// 暂时仓库不让改,如果供货那边改了导致和活动不一致,就直接报错
	brandDistributeWarehousePtrs, err := s.brandService.ListBrandDistributeWarehousePtrsByBrandID(ctx, q, brandID)
	if err != nil {
		return
	}
	if len(brandDistributeWarehousePtrs) == 0 {
		err = fmt.Errorf("供货门店没有设置")
		return
	}
	warehouseIDsFromBrand := util.MapToInt64(util.ToGenericSlice(brandDistributeWarehousePtrs), func(i interface{}) int64 {
		v := i.(*sqlc.BrandDistributeWarehouse)
		return v.WarehouseID
	})
	// 更新前置分析
	normalActivityWarehousesExist, err := q.ListNormalBrandActivityWarehousesByActivityID(ctx, activityID)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	for _, w := range normalActivityWarehousesExist {
		if !util.Int64Contains(warehouseIDsFromBrand, w.WarehouseID) {
			err = fmt.Errorf("供货仓库有变化,无法修改")
			return
		}
		activityWarehouses = append(activityWarehouses, w)
	}

	var newActivitySkuStorageKeys []string
	newProductIDsOfActivity := util.MapToInt64(util.ToGenericSlice(req.ActivityProductWithSkus), func(v interface{}) int64 {
		val := v.(*activityGrpc.ActivityProductWithSkuEntity)
		currentSkuStorageKeys := util.MapToString(util.ToGenericSlice(val.Skus), func(v interface{}) string {
			val := v.(*activityGrpc.ActivitySkuEntity)
			return fmt.Sprintf("%d-%d", val.ProductId, val.ProductSpecId)
		})
		newActivitySkuStorageKeys = append(newActivitySkuStorageKeys, currentSkuStorageKeys...)
		return val.ProductId
	})
	normalActivityProductsExist, err := q.ListNormalActivityProductsByActivityID(ctx, activityID)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	// 找出需要disabled的activityProducts
	var toDisableActivityProductIDs []int64
	toDisableProductIDsOfActivityProduct := util.MapToInt64(util.Filter(util.ToGenericSlice(normalActivityProductsExist), func(v interface{}) bool {
		val := v.(sqlc.ActivityProduct)
		return !util.Int64Contains(newProductIDsOfActivity, val.ProductID)
	}), func(v interface{}) int64 {
		val := v.(sqlc.ActivityProduct)
		toDisableActivityProductIDs = append(toDisableActivityProductIDs, val.ID)
		return val.ProductID
	})
	slog.Infof("toDisableActivityProductIDs->%v", toDisableActivityProductIDs)
	slog.Infof("toDisableProductIDsOfActivityProduct->%v", toDisableProductIDsOfActivityProduct)

	// 找出需要disabled的activitySkus
	// a (产品还在,规格没了)
	normalActivitySkusExist, err := q.ListNormalActivitySkusByActivityID(ctx, activityID)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	toDisableActivitySkuIDs1 := util.MapToInt64(util.Filter(util.ToGenericSlice(normalActivitySkusExist), func(v interface{}) bool {
		val := v.(sqlc.ActivitySku)
		return !util.StringContains(newActivitySkuStorageKeys, fmt.Sprintf("%d-%d", val.ProductID, val.ProductSpecID.Int64))
	}), func(v interface{}) int64 {
		val := v.(sqlc.ProductAttributeValue)
		return val.ID
	})
	slog.Infof("toDisableActivitySkuIDs1->%v", toDisableActivitySkuIDs1)

	// b (产品没了,规格也就没了)
	normalActivitySkusOfToDisableActivityProduct, err := q.ListNormalActivitySkusByActivityIDAndProductIDs(ctx,
		sqlc.ListNormalActivitySkusByActivityIDAndProductIDsParams{
			ActivityID: activityID,
			Column2:    toDisableProductIDsOfActivityProduct,
		})
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	toDisableActivitySkuIDs2 := util.MapToInt64(util.ToGenericSlice(normalActivitySkusOfToDisableActivityProduct), func(v interface{}) int64 {
		val := v.(sqlc.ActivitySku)
		return val.ID
	})
	slog.Infof("toDisableActivitySkuIDs2->%v", toDisableActivitySkuIDs2)

	toDisableActivitySkuIDs := append(toDisableActivitySkuIDs1, toDisableActivitySkuIDs2...)
	slog.Infof("toDisableActivitySkuIDs->%v", toDisableActivitySkuIDs)

	if activityService.CheckCanEndorse(activity.Type) {
		// 找出需要disabled的activityConsignmentBonusSettings
		newProductIdsOfConsignmentBonusSetting := util.MapToInt64(util.ToGenericSlice(req.Consignment.ConsignmentBonusSettings), func(v interface{}) int64 {
			val := v.(*activityGrpc.ActivityConsignmentBonusSettingEntity)
			return val.ProductId
		})

		// a (产品还在,帮卖奖励设置没了)
		normalActivityConsignmentBonusSettingsExist, err2 := q.ListNormalActivityConsignmentBonusSettingsByActivityID(ctx, activityID)
		if err != nil {
			res.OpRet = &message.OperationResult{
				Success: false,
				Code:    400,
				Msg:     err2.Error(),
			}
			return
		}
		toDisableActivityConsignmentBonusSettingIDs1 := util.MapToInt64(util.Filter(util.ToGenericSlice(normalActivityConsignmentBonusSettingsExist), func(v interface{}) bool {
			val := v.(sqlc.ActivityConsignmentBonusSetting)
			return !util.Int64Contains(newProductIdsOfConsignmentBonusSetting, val.ProductID)
		}), func(v interface{}) int64 {
			val := v.(sqlc.ProductAttributeValue)
			return val.ID
		})
		slog.Infof("toDisableActivityConsignmentBonusSettingIDs1->%v", toDisableActivityConsignmentBonusSettingIDs1)

		// b (产品没了,帮卖奖励设置也就没了)
		normalActivityConsignmentBonusSettingsOfToDisableActivityProduct, err3 := q.ListNormalActivityConsignmentBonusSettingsByActivityIDAndProductIDs(ctx,
			sqlc.ListNormalActivityConsignmentBonusSettingsByActivityIDAndProductIDsParams{
				ActivityID: activityID,
				Column2:    toDisableProductIDsOfActivityProduct,
			})
		if err != nil {
			res.OpRet = &message.OperationResult{
				Success: false,
				Code:    400,
				Msg:     err3.Error(),
			}
			return
		}
		toDisableActivityConsignmentBonusSettingIDs2 := util.MapToInt64(util.ToGenericSlice(normalActivityConsignmentBonusSettingsOfToDisableActivityProduct), func(v interface{}) int64 {
			val := v.(sqlc.ActivityConsignmentBonusSetting)
			return val.ID
		})
		slog.Infof("toDisableActivityConsignmentBonusSettingIDs2->%v", toDisableActivityConsignmentBonusSettingIDs2)

		toDisableActivityConsignmentBonusSettingIDs = append(toDisableActivityConsignmentBonusSettingIDs1, toDisableActivityConsignmentBonusSettingIDs2...)
		slog.Infof("toDisableActivityConsignmentBonusSettingIDs->%v", toDisableActivityConsignmentBonusSettingIDs)
	}
	slog.Info("更新前置分析完毕")

	err = s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {

		begined := req.Begined
		ended := req.Ended
		maxiumPurchaseFrequency := req.MaxiumPurchaseFrequency
		minimumPurchaseAmount := req.MinimumPurchaseAmount

		p := sqlc.UpdateActivityOfStoreParams{
			ID:                      activityID,
			Name:                    req.Name,
			Intro:                   json.RawMessage(req.Intro),
			Begined:                 sqlc.PBTimestampToGoTime(begined),
			Ended:                   sqlc.PBTimestampToGoTime(ended),
			Background:              sqlc.NSFromString(req.Background),
			SaleMode:                req.SaleMode,
			MaxiumPurchaseFrequency: sqlc.NInt32FromInt32(maxiumPurchaseFrequency),
			MinimumPurchaseAmount:   minimumPurchaseAmount,
			Tel:                     req.Tel,
		}

		if len(req.TimeRanges) > 0 {
			var timeRanges []ctype.TimeRange
			for _, tr := range req.TimeRanges {
				timeRanges = append(timeRanges, ctype.TimeRange{
					BeginAt: tr.BeginAt,
					EndAt:   tr.EndAt,
				})
			}
			p.TimeRanges = timeRanges
		}

		activity, err = q.UpdateActivityOfStore(ctx, p)
		if err != nil {
			return err
		}
		slog.Info("1更新门店活动 ok")

		existEndorsedActivity, err := q.CheckExistActivitiesOfSpokesmanOwnActivityByParentActivityID(ctx, sqlc.NInt64FromInt64(activity.ID))
		if err != nil {
			return
		}
		if existEndorsedActivity {
			err = q.SyncActivityInfoFromParentActivityByParentActivityID(ctx, sqlc.SyncActivityInfoFromParentActivityByParentActivityIDParams{
				ParentID:                sqlc.NInt64FromInt64(activity.ID),
				Begined:                 sqlc.PBTimestampToGoTime(begined),
				Ended:                   sqlc.PBTimestampToGoTime(ended),
				MaxiumPurchaseFrequency: sqlc.NInt32FromInt32(maxiumPurchaseFrequency),
				MinimumPurchaseAmount:   minimumPurchaseAmount,
			})
			if err != nil {
				return
			}
		}

		if len(toDisableActivityProductIDs) > 0 {
			disableActivityProductRet, err := q.DisableActivityProductByIDs(ctx, toDisableActivityProductIDs)
			if err != nil {
				return err
			}
			slog.Infof("4 disable活动B商品 ok %v", disableActivityProductRet)
		} else {
			slog.Info("4 没有要disable的活动B商品 ok")
		}

		if len(toDisableActivitySkuIDs) > 0 {
			disableActivitySkuRet, err := q.DisableActivitySkuByIDs(ctx, toDisableActivitySkuIDs)
			if err != nil {
				return err
			}
			slog.Infof("5 disable活动B sku ok %v", disableActivitySkuRet)
		} else {
			slog.Info("5 没有要disable的活动B sku ok")
		}
		// 活动商品、商品规格
		var activityProduct sqlc.ActivityProduct
		// var activitySku sqlc.ActivitySku
		for idx1, item1 := range req.ActivityProductWithSkus {
			maxSku, minSku := util.MaxWithMin(util.ToGenericSlice(item1.Skus), func(i1, i2 interface{}) util.Ord {
				ps1, ps2 := i1.(*activityGrpc.ActivitySkuEntity), i2.(*activityGrpc.ActivitySkuEntity)
				return (util.IfThenElse(ps1.Price-ps2.Price < 0, util.Lt, util.Gt)).(util.Ord)
			})
			priceMax, priceMin := maxSku.(*activityGrpc.ActivitySkuEntity).Price, minSku.(*activityGrpc.ActivitySkuEntity).Price
			slog.Infof("max,min %v, %v", priceMax, priceMin)
			quantity := util.Int32IfThenElse(item1.Quantity == 0, 1, item1.Quantity)

			activityProduct, err = q.UpsertActivityProduct(ctx, sqlc.UpsertActivityProductParams{
				BrandID:          activity.BrandID,
				ActivityID:       activity.ID,
				RootActivityID:   activity.RootID,
				ParentActivityID: activity.ParentID,
				ProductID:        item1.ProductId,
				IsShow:           item1.IsShow,
				PriceMin:         priceMin,
				PriceMax:         priceMax,
				Quantity:         quantity,
				SortNo:           int32(idx1 + 1),
			})
			if err != nil {
				return err
			}
			// activityProducts = append(activityProducts, activityProduct)

			for idx2, item2 := range item1.Skus {
				slog.Infof("item2.ProductId:%d, item2.ProductSpecId:%d", item2.ProductId, item2.ProductSpecId)
				_, err = q.UpsertActivitySku(ctx, sqlc.UpsertActivitySkuParams{
					BrandID:           activity.BrandID,
					ActivityID:        activity.ID,
					RootActivityID:    activity.RootID,
					ParentActivityID:  activity.ParentID,
					ActivityProductID: activityProduct.ID,
					ProductID:         item2.ProductId,
					ProductSpecID:     sqlc.NInt64FromInt64(item2.ProductSpecId),
					Price:             item2.Price,
					Maximum:           util.Int32IfThenElse(item2.Maximum == 0, -1, item2.Maximum),
					ProductSpecKey:    fmt.Sprintf("%d-%d", item2.ProductId, item2.ProductSpecId),
					SortNo:            int32(idx2 + 1),
				})

				if err != nil {
					return err
				}

				// activitySkus = append(activitySkus, activitySku)
			}
		}
		slog.Infof("6 设置活动B商品、商品规格 ok")

		_, err = q.UpdateActivitySkuStoreKeyByActivityID(ctx, activity.ID)
		if err != nil {
			return err
		}
		slog.Infof("6.5 更新storageKey ok")

		if activityService.CheckCanEndorse(activity.Type) {
			// 活动代卖设置
			activityConsignment, err := q.UpsertActivityConsignment(ctx, sqlc.UpsertActivityConsignmentParams{
				ActivityID:           activity.ID,
				AllowHiddenProduct:   req.Consignment.AllowHiddenProduct,
				AllowAllSpokesman:    req.Consignment.AllowAllSpokesman,
				SpecificSpokesmanIds: req.Consignment.SpecificSpokesmanIds,
				// SpecificSpokesmans: trans.MapMiniItemFromPb(req.Consignment.SpecificSpokesmans, trans.MiniItemFromPb),
			})
			if err != nil {
				return err
			}
			activityConsignmentPtr = &activityConsignment
			slog.Info("7 设置活动B帮卖 ok")

			if len(toDisableActivityConsignmentBonusSettingIDs) > 0 {
				disableActivityConsignmentBonusSettingRet, err := q.DisableActivityConsignmentBonusSettingByIDs(ctx, toDisableActivityConsignmentBonusSettingIDs)
				if err != nil {
					return err
				}
				slog.Infof("8 disable活动B代卖奖励设置 ok %v", disableActivityConsignmentBonusSettingRet)
			} else {
				slog.Info("8 没有要disable的活动B代卖奖励设置 ok")
			}

			//3 活动代卖奖励设置
			var bonusSetting sqlc.ActivityConsignmentBonusSetting
			for _, item := range req.Consignment.ConsignmentBonusSettings {
				bonusSetting, err = q.UpsertActivityConsignmentBonusSetting(ctx, sqlc.UpsertActivityConsignmentBonusSettingParams{
					ActivityID: activity.ID,
					ProductID:  item.ProductId,
					BonusRate:  item.BonusRate,
				})
				if err != nil {
					return err
				}
				activityConsignmentBonusSettings = append(activityConsignmentBonusSettings, bonusSetting)
			}
			slog.Info("9 设置活动B帮卖奖励 ok")
		}

		activityProductWithProductInfos, activitySkuWithProductOrProductSpecInfos, err = s.activityService.FetchNormalProductAndSkus(ctx, q, activity.ID)
		if err != nil {
			return err
		}
		slog.Info("10 读取活动B产品及sku信息 ok ok")

		// 更新活动
		if activity.InventoryFreezeNo.Valid {
			activityWarehouse := activityWarehouses[0]
			warehouseID := activityWarehouse.WarehouseID
			err = s.activityService.UpdateInventoryFreezeOrderForB(ctx, &activity, activitySkuWithProductOrProductSpecInfos, warehouseID)
			if err != nil {
				return err
			}
		}

		return
	})

	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	s.activityService.ClearCacheOwn(activity.ID)
	res.ActivityOfStore = transformActivityOfStoreEntityToPb(activityProductWithProductInfos, activitySkuWithProductOrProductSpecInfos, activityConsignmentPtr, activityConsignmentBonusSettings)(activity)
	return
}

// PublishActivityOfB 发布品牌活动B
func (s *ActivityAPIService) PublishActivityOfStore(ctx context.Context, req *activityGrpc.PublishActivityOfStoreRequest) (res *activityGrpc.PublishActivityOfStoreResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("activityStore->PublishActivityOfStore:%v", req)
	res = &activityGrpc.PublishActivityOfStoreResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}
	q := s.dao.Q
	activityID := req.Id
	storeID := req.StoreId
	activity, err := q.GetActivity(ctx, activityID)
	if err != nil {
		return
	}

	if !activity.StoreID.Valid || activity.StoreID.Int64 != storeID {
		err = fmt.Errorf("活动不属于当前门店")
		return
	}

	if activity.Owner != string(sqlc.EmActivityOwnerStore) {
		err = fmt.Errorf("活动owner[%s]不符合", activity.Owner)
		return
	}
	if activity.ProcessStatus != string(sqlc.EmActivityProcessStatusDraft) {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     fmt.Sprintf("活动状态[%s]不允许发布", activity.ProcessStatus),
		}
		return
	}

	var toProcessStatus string

	err = s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {

		cActivity := s.cacher.FetchActivity(activity.ID)
		if cActivity == nil {
			err = fmt.Errorf("not found activity:%d", activity.ID)
			return
		}

		if len(cActivity.WarehouseIDs) != 1 {
			err = fmt.Errorf("too many activity warehouseIDs:%d,%v", activity.ID, cActivity.WarehouseIDs)
			return
		}

		warehouseID := cActivity.WarehouseIDs[0]

		skus, err := s.activityService.FetchNormalSkus(ctx, q, activity.ID)
		if err != nil {
			return
		}

		toProcessStatus, err = s.activityService.PublishActivity(ctx, q, activityService.PublishActivityParam{
			ActivityPtr: &activity,
			UserID:      req.UserId,
			WarehouseID: warehouseID,
			Skus:        skus,
		})
		if err != nil {
			return err
		}
		slog.Infof("a 活动状态改为运行中 ok")
		err = s.activityService.UpdateActivityTypeInfoSinceActivityPublished(ctx, q, &activity)
		if err != nil {
			return err
		}
		slog.Infof("b 活动发布成功后更新类型信息 ok")

		asFeatureTypes := activityService.ActivitiesAsFeature
		err = s.storeService.UpdateStoreStatPublishActivityCountSinceStoreOwnActivityPublished(ctx, q, storeID, asFeatureTypes)
		if err != nil {
			return err
		}
		slog.Infof("c 活动发布成功后更新门店统计已发布活动数量 ok")

		return
	})

	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	s.activityService.ClearCacheOwn(activity.ID)
	res.ProcessStatus = toProcessStatus
	return
}

/****************************************************************************************/
/*                               store activity entity of query                         */
/****************************************************************************************/

// GetActivityOfStore 获取单个门店活动
func (s *ActivityAPIService) GetActivityOfStore(ctx context.Context, req *activityGrpc.GetActivityOfStoreRequest) (res *activityGrpc.GetActivityOfStoreResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("activityStore->GetActivityOfStore:%v", req)
	res = &activityGrpc.GetActivityOfStoreResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	q := s.dao.Q
	activityID := req.Id
	storeID := req.StoreId
	activity, err := q.GetActivity(ctx, activityID)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}

	if !activity.StoreID.Valid || activity.StoreID.Int64 != storeID {
		err = fmt.Errorf("活动不属于当前门店")
		return
	}

	normalActivityProductWithProductInfos, normalActivitySkus, err := s.activityService.FetchNormalProductAndSkus(ctx, q, activity.ID)
	if err != nil {
		res.OpRet = &message.OperationResult{
			Success: false,
			Code:    400,
			Msg:     err.Error(),
		}
		return
	}
	// slog.Infof("normalActivitySkus=>%d", len(normalActivitySkus))
	var activityConsignmentPtr *sqlc.ActivityConsignment
	var normalActivityConsignmentBonusSettings []sqlc.ActivityConsignmentBonusSetting
	if activityService.CheckCanEndorse(activity.Type) {
		activityConsignment, err2 := q.GetActivityConsignmentByActivityID(ctx, activity.ID)
		if err2 != nil {
			res.OpRet = &message.OperationResult{
				Success: false,
				Code:    400,
				Msg:     err2.Error(),
			}
			return
		}
		activityConsignmentPtr = &activityConsignment

		normalActivityConsignmentBonusSettings, err = q.ListNormalActivityConsignmentBonusSettingsByActivityID(ctx, activity.ID)
		if err != nil {
			res.OpRet = &message.OperationResult{
				Success: false,
				Code:    400,
				Msg:     err.Error(),
			}
			return
		}
	}

	res.ActivityOfStore = transformActivityOfStoreEntityToPb(normalActivityProductWithProductInfos, normalActivitySkus, activityConsignmentPtr, normalActivityConsignmentBonusSettings)(activity)
	return
}

/****************************************************************************************/
/*                               store activity joins entity of helper                  */
/****************************************************************************************/

func transformListActivityItemOfStoreWithStatByStoreIDRowEntityToPb(ptr *sqlc.ListActivityItemOfStoreWithStatsByStoreIDRow) *activityGrpc.ActivityItemOfStoreWithStatEntity {
	return &activityGrpc.ActivityItemOfStoreWithStatEntity{
		// activity
		Id:            ptr.ID,
		Modified:      sqlc.GoTimeToPBTimestamp(ptr.Modified),
		BrandId:       ptr.BrandID,
		PubUserId:     ptr.PubUserID,
		Name:          ptr.Name,
		Type:          ptr.Type,
		Intro:         string(ptr.Intro),
		ProcessStatus: ptr.ProcessStatus,
		Published:     sqlc.GoNTimeToPBTimestamp(ptr.Published),
		Begined:       sqlc.GoTimeToPBTimestamp(ptr.Begined),
		Ended:         sqlc.GoTimeToPBTimestamp(ptr.Ended),
		TimeRanges:    transformTimeRangeEntityToPb(ptr.TimeRanges),
		StoreId:       ptr.StoreID.Int64,
		DistributorId: ptr.DistributorID.Int64,
		SaleMode:      ptr.SaleMode,

		// activity_stat
		OrderCount:     ptr.OrderCount,
		LatestAccessed: sqlc.GoTimeToPBTimestamp(ptr.LatestAccessed),
		Pv:             ptr.Pv,
		Uv:             ptr.Uv,
		Iv:             ptr.Iv,
	}
}

func mapActivityItemOfStoreWithStatByStoreIDEntityToPb(list []*sqlc.ListActivityItemOfStoreWithStatsByStoreIDRow, f func(*sqlc.ListActivityItemOfStoreWithStatsByStoreIDRow) *activityGrpc.ActivityItemOfStoreWithStatEntity) []*activityGrpc.ActivityItemOfStoreWithStatEntity {
	r := make([]*activityGrpc.ActivityItemOfStoreWithStatEntity, len(list))
	for i, item := range list {
		r[i] = f(item)
	}
	return r
}

/****************************************************************************************/
/*                               store activity joins entity of query                  */
/****************************************************************************************/

// ListActivityItemOfStoreWithStatsByStoreID 获取门店活动Item+统计列表
func (s *ActivityAPIService) ListActivityItemOfStoreWithStatsByStoreID(ctx context.Context, req *activityGrpc.ListActivityItemOfStoreWithStatsByStoreIDRequest) (res *activityGrpc.ListActivityItemOfStoreWithStatsByStoreIDResponse, err error) {
	slog := s.NewContextLogger(ctx)
	slog.Infof("activityStore->ListActivityItemOfStoreWithStatsByStoreID:%v", req)
	res = &activityGrpc.ListActivityItemOfStoreWithStatsByStoreIDResponse{
		OpRet: &message.OperationResult{
			Success: true,
			Code:    0,
		},
	}

	q := s.dao.Q
	ts := sqlc.PBTimestampToGoTime(req.Ts)
	storeID := req.StoreId
	total, ptrs, err := s.activityService.ListActivityWithStatPtrsOfStore(ctx, q, &util.Pager{
		Ts:     ts,
		Limit:  req.Limit,
		Offset: req.Offset,
	}, &activityService.ListActivityWithStatPtrsOfStoreParam{
		StoreID:         storeID,
		IsAll:           req.IsAll,
		ProcessStatuses: req.ProcessStatuses,
		Keyword:         req.Keyword,
	})

	if err != nil {
		return
	}
	if total == 0 {
		// 不用继续调用listfunc
		return
	}

	res.Total = total
	res.ActivityItemOfStoreWithStats = mapActivityItemOfStoreWithStatByStoreIDEntityToPb(ptrs, transformListActivityItemOfStoreWithStatByStoreIDRowEntityToPb)
	return
}